package microarray;

import java.io.*;
import java.net.*;
import java.util.*;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class Platform extends MicroArrayElement
{
	private Stack<String> sample_ids;
	// private Stack<String> dataset_ids;
	private Stack<DataSet> datasets;
	private HashMap<String, Boolean> controlMap;
	private HashMap<String, String> geneMap;

	// note on the retmax argument: I picked a big number, but no platform has more than about 300 GDS returned
	// http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=gds&term=GPL32[ACCN]+AND+gds[ETYP]&retmax=100000
	private final String eSearchUrlString = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=gds&term=ID_HERE[ACCN]+AND+gds[ETYP]&retmax=100000";

	public Platform(String id, MicroArrayCrawler parent) throws IOException
	{
		super(id);
		this.parent = parent;
		this.sample_ids = new Stack<String>();
		this.datasets = new Stack<DataSet>();
		this.controlMap = new HashMap<String, Boolean>();
	}

	public URL getESearchURL()
	{
		try
		{
			return new URL(eSearchUrlString.replace("ID_HERE", id));
		} catch (IOException e)
		{
			System.out.println("Error updating URL " + e);
			return null;
		}
	}

	public String getGene(String geneID)
	{
		return geneMap.get(geneID);
	}

	public String getDir()
	{
		return (parent.getDir() + id + "/");
	}

	private void addSample(String sample_id)
	{
		sample_ids.push(sample_id);
	}

	private void addDataSet(String datasetId) throws IOException
	{
		datasets.push(new DataSet(datasetId, this));
	}

	public boolean isControl(String sampleId)
	{
		Boolean x = controlMap.get(sampleId);
		if (x == null)
		{
			return false;
		}
		return controlMap.get(sampleId);
	}

	public void setControl(String sampleId, boolean isControl)
	{
		controlMap.put(sampleId, isControl);
	}

	public void record() throws IOException
	{
		// RECORD
		new File(getDir()).mkdirs();
		recordGeneMap();
	}

	public void recordGeneMap() throws IOException
	{
		File map = new File(getDir() + id + "_map.txt");
		printDebug("writing to " + map);
		FileOutputStream out = new FileOutputStream(map);
		PrintStream p = new PrintStream(out);
		for (String geneID : geneMap.keySet())
		{
			p.println(geneID + "\t" + geneMap.get(geneID));
		}
		p.close();
	}

	public void recordDataSetList() throws IOException
	{
		File gdsFile = new File(getDir() + id + "_gds.txt");
		printDebug("writing to " + gdsFile);
		FileOutputStream out = new FileOutputStream(gdsFile);
		PrintStream p = new PrintStream(out);
		while (!datasets.empty())
		{
			DataSet d = datasets.pop();
			p.println(d.getID() + '\t' + d.getTitle() + '\t'
					+ d.getDescription());
		}
	}

	public void scrapeAndRecord() throws IOException
	{
		printDebug("scraping specifics " + id + ": " + getDataURL().getQuery());
		new PlatformScraper().doDownload(id, 5000, 10);

		record();

		for (DataSet dataset : datasets)
		{
			try
			{
				dataset.scrapeAndRecord();
			} catch (IOException e)
			{
				System.out.println("scrape/record dataset " + dataset
						+ " failed: " + e);
			}
		}
		recordDataSetList();

		Sample sample;

		while (!sample_ids.empty())
		{
			sample = new Sample(sample_ids.pop(), this);
			try
			{
				sample.scrapeAndRecord();
			} catch (IOException e)
			{
				System.out.println("scrape/record sample " + sample
						+ " failed: " + e);
			}
		}
	}

	public void build() throws IOException
	{

		new DataSetsDownloader().doDownload(id, 5000, 10);
		printDebug("scraping general info " + id + ": "
				+ getGeneralURL().getQuery());
		new PlatformDownloader().doDownload(id, 5000, 10);
	}

	private class PlatformScraper extends Downloader
	{
		public void download() throws IOException
		{
			URLConnection c = getDataURL().openConnection();
			BufferedReader r = new BufferedReader(new InputStreamReader(c
					.getInputStream()));
			String line = r.readLine();
			boolean inTable = false;
			while (line != null && !inTable)
			{
				inTable = line.contains("!platform_table_begin");
				line = r.readLine();
			}
			if (inTable) // burn the header row
				line = r.readLine();

			geneMap = new HashMap<String, String>();
			String id;
			String gene;
			while (line != null && inTable)
			{
				inTable = !line.contains("!platform_table_end");
				if (inTable)
				{
					String[] row = line.split("\t");
					try
					{
						id = row[0].toUpperCase();
						gene = row[1].toUpperCase();
						if (gene.equals(""))
							geneMap.put(id, id);
						else
							geneMap.put(id, gene);
					} catch (ArrayIndexOutOfBoundsException e)
					{
						System.out.println("incomplete line: " + line);
					}
					// System.out.println(id + "\t" + gene);
					line = r.readLine();
				}
			}
		}
	}

	private class PlatformDownloader extends Downloader
	{
		public void download() throws IOException
		{
			URLConnection c = getGeneralURL().openConnection();
			BufferedReader r = new BufferedReader(new InputStreamReader(c
					.getInputStream()));
			String samplePrefix = "!Platform_sample_id = ";
			for (String line = r.readLine(); line != null; line = r.readLine())
			{
				if (line.contains(samplePrefix))
				{
					addSample(line.substring(samplePrefix.length()));
				}
			}
		}
	}

	private class DataSetsDownloader extends Downloader
	{
		public void download() throws IOException
		{
			printDebug("scraping datasets " + id + ": "
					+ getESearchURL().getQuery());
			URLConnection c = getESearchURL().openConnection();
			// BufferedReader r = new BufferedReader(new
			// InputStreamReader(c.getInputStream()));

			SAXParserFactory spf = SAXParserFactory.newInstance();
			DataSetXMLParser d = new DataSetXMLParser();
			try
			{
				spf.newSAXParser().parse(c.getInputStream(), d);
			} catch (ParserConfigurationException e)
			{
				e.printStackTrace();
			} catch (SAXException e)
			{
				e.printStackTrace();
			}
			// System.out.println(d);
			for (String id : d.getIDs())
			{
				addDataSet("GDS" + id);
			}
		}
	}

	private class DataSetXMLParser extends DefaultHandler
	{
		String textValue;
		int count;
		String webEnv;
		String queryKey;
		ArrayList<String> ids = new ArrayList<String>();

		public ArrayList<String> getIDs()
		{
			return ids;
		}

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException
		{

		}

		public void characters(char[] ch, int start, int length)
				throws SAXException
		{
			textValue = new String(ch, start, length);
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException
		{
			if (qName.equalsIgnoreCase("Count"))
			{
				count = Integer.parseInt(textValue);
			} else if (qName.equals("WebEnv"))
			{
				webEnv = textValue;
			} else if (qName.equals("QueryKey"))
			{
				queryKey = textValue;
			} else if (qName.equals("Id"))
			{
				ids.add(textValue);
			}
		}

		public String toString()
		{
			String out = webEnv;
			out += "\n" + queryKey;
			out += "\ncount=" + count;
			for (String id : ids)
				out += id + "\n";
			return out;
		}
	}

}